﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.IO;
using System.Diagnostics;

namespace SignalLab.BaseLibrary
{
  public class WavHelper
  {
    public const int HEADER_LENGTH = 44;

    /// <summary>
    /// Write down the WAV header to the stream. Header should be followed by raw data.
    /// Refer to http://technology.niagarac.on.ca/courses/ctec1631/WavFileFormat.html
    /// for details on the format.
    /// </summary>
    /// <param name="outputStream"></param>
    /// <param name="rawLength">length of byte array</param>
    public static void WriteWavHeader(Stream outputStream, int rawLength, int sampleRate, int bitsPerSample, int channels)
    {
      if (outputStream.Position != 0)
      {
        Debug.WriteLine("OutputStream position is not 0 when writing WavHeader!");
      }

      BinaryWriter bwOutput = new BinaryWriter(outputStream);


      // Note that we use ToCharArray() when writing fixed strings
      // to force using the char[] overload because
      // Write(string) writes the string prefixed by its length.

      // -- RIFF chunk

      bwOutput.Write("RIFF".ToCharArray());

      // Total Length Of Package To Follow
      // Computed as data length plus the header length without the data
      // we have written so far and this data (44 - 4 ("RIFF") - 4 (this data))
      bwOutput.Write((uint)(rawLength + 36));
      bwOutput.Write("WAVE".ToCharArray());

      // -- FORMAT chunk
      bwOutput.Write("fmt ".ToCharArray());

      // Length Of FORMAT Chunk (Binary, always 0x10)
      bwOutput.Write((uint)0x10);

      // Always 0x01
      bwOutput.Write((ushort)0x01);

      // Channel Numbers (Always 0x01=Mono, 0x02=Stereo)
      bwOutput.Write((ushort)1);

      // Sample Rate (Binary, in Hz)
      bwOutput.Write(sampleRate);

      // Bytes Per Second
      bwOutput.Write((uint)(bitsPerSample * sampleRate * channels / 8));

      // Bytes Per Sample: 1=8 bit Mono, 2=8 bit Stereo or 16 bit Mono, 4=16 bit Stereo
      bwOutput.Write((ushort)(bitsPerSample * channels / 8));

      // Bits Per Sample
      bwOutput.Write((ushort)bitsPerSample);

      // -- DATA chunk
      bwOutput.Write("data".ToCharArray());

      // Length Of Data To Follow
      bwOutput.Write((uint)rawLength);
    }


    //public static void WriteWavHeader(Stream outputStream, int rawLength, int sampleRate, int bitsPerSample, int channels)
    //{
    //  BinaryWriter bw = new BinaryWriter(outputStream);
    //  bw.Write(new char[4] { 'R', 'I', 'F', 'F' });

    //  bw.Write(36 + rawLength);

    //  bw.Write(new char[8] { 'W', 'A', 'V', 'E', 'f', 'm', 't', ' ' });

    //  bw.Write((int)16);

    //  bw.Write((short)1);
    //  bw.Write(channels);

    //  bw.Write(sampleRate);

    //  bw.Write((int)(sampleRate * ((bitsPerSample * channels) / 8)));

    //  bw.Write((short)((bitsPerSample * channels) / 8));

    //  bw.Write(bitsPerSample);

    //  bw.Write(new char[4] { 'd', 'a', 't', 'a' });
    //  bw.Write(rawLength);
    //}

    public static void SavePcmToWav(Stream rawData, Stream output, int rawLength, int sampleRate, int bitsPerSample, int channels)
    {
      WriteWavHeader(output, rawLength, sampleRate, bitsPerSample, channels);
      // Raw PCM data follows...

      // Reset position in rawData and remember its origin position to restore at the end.
      long originalRawDataStreamPosition = rawData.Position;
      rawData.Seek(0, SeekOrigin.Begin);

      BinaryWriter bwOutput = new BinaryWriter(output);
      // Append all data from rawData stream into output stream.
      byte[] buffer = new byte[4096];
      int read;       // number of bytes read in one iteration
      while ((read = rawData.Read(buffer, 0, 4096)) > 0)
      {
        bwOutput.Write(buffer, 0, read);
      }

      rawData.Seek(originalRawDataStreamPosition, SeekOrigin.Begin);
    }

    public static void AppendPcmToWav(Stream orinalStream, byte[] rawDataToAppend)
    {
      BinaryWriter bwOutput = new BinaryWriter(orinalStream);
      
      // Append all data from rawData stream into output stream.
      bwOutput.Write(rawDataToAppend, 0, rawDataToAppend.Length);
    }
  }
}
